#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
Simplified BSD License (BSD)
Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this 
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 09-Nov-09 | HuyHM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.ComponentModel;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;

using Thn;
#endregion

namespace Thn.Interface.Vcl
{
    /// <summary>
    /// Control to display a list of item.
    /// </summary>
    [ToolboxItem(true), DefaultEvent("SelectedIndexChanged"), DefaultProperty("Items"), ToolboxBitmap(typeof(ResourceBeacon), "Thn.Interface.Vcl.Resources.Icons.Listbox.bmp")]
#if DEBUG
    public
#else
    public
#endif
 class ListBox : ControlBase,IListBoxHost
    {
        #region EVENTS

        #region Item Clicked
        /// <summary>
        /// This event is raised after item has been clicked.
        /// This may or may not raise with event SelectedIndexChanged.
        /// </summary>
        public event EventHandler ItemClicked;

        /// <summary>
        /// Invoke event ItemClicked.
        /// </summary>
        protected void RaiseItemClicked(EventArgs args)
        {
            EventHandler handler = ItemClicked;
            if (handler != null) handler(this, args);
        }
        #endregion

        #region Selected Index Changed
        /// <summary>
        /// This event is raised after 
        /// </summary>
        public event EventHandler SelectedIndexChanged;

        /// <summary>
        /// Invoke event SelectedIndexChanged
        /// </summary>
        protected void RaiseSelectedIndexChanged(EventArgs args)
        {
            EventHandler handler = SelectedIndexChanged;
            if (handler != null) handler(this, args);
        }
        #endregion

        #endregion

        #region Fields
        #region For flattern
        /// <summary>
        /// Need to flattern the
        /// </summary>
        protected bool mReflattenItems = false;
        #endregion
        #endregion

        #region Properties

        #region Items
        private ListBoxItemList mItems = null;
        /// <summary>
        /// Gets/Sets list box's items.
        /// </summary>
        [Browsable(true),DefaultValue(null),DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Category("Value")]
        public ListBoxItemList Items
        {
            get
            {
                if (mItems == null)
                {
                    mItems = new ListBoxItemList();
                    mItems.mParentListbox = this;
                }
                return mItems;
            }
            set
            {
                mItems = value;
                mItems.mParentListbox = this;
                mReflattenItems = true;
                // invalidate layout
                InvalidateLayout();
            }
        }
        #endregion

        #region Columns
        private ListBoxColumnList mColumns = null;
        /// <summary>
        /// Gets/Sets listbox's columns.
        /// </summary>
        [Browsable(true),DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Category("Appearance")]
        public ListBoxColumnList Columns
        {
            get
            {
                if (mColumns == null)
                {
                    mColumns = new ListBoxColumnList();
                }
                #region Defensive
                else
                {
                    // do nothing.
                }
                #endregion
                return mColumns;
            }
            set
            {
                mColumns = value;
                InvalidateLayout();
            }
        }
        #endregion

        #region Row Height
        private int mRowHeight = 24;
        /// <summary>
        /// Gets/Sets height of each row.
        /// </summary>
        [Browsable(true),DefaultValue(24),Category("Appearance")]
        public int RowHeight
        {
            get { return mRowHeight; }
            set
            {
                mRowHeight = value;

                // invalidate layout
                InvalidateLayout();
            }
        }
        #endregion

        #region Draw Title
        private bool mDrawTitle = false;
        /// <summary>
        /// Gets/Sets whether draw title of columns.
        /// </summary>
        [Browsable(true),DefaultValue(false),Category("Appearance")]
        public bool DrawTitle
        {
            get { return mDrawTitle; }
            set
            {
                mDrawTitle = value;

                // invalidate layout
                InvalidateLayout();
            }
        }
        #endregion

        #region Title Height
        private int mTitleHeight = 24;
        /// <summary>
        /// Gets/Sets height of column title row.
        /// </summary>
        [Browsable(true), DefaultValue(24), Category("Appearance")]
        public int TitleHeight
        {
            get { return mTitleHeight; }
            set
            {
                mTitleHeight = value;

                // invalidate layout
                InvalidateLayout();
            }
        }
        #endregion

        #region Text Style
        private TextStyle mTextStyle = TextStyle.Normal;
        /// <summary>
        /// Gets/Sets font style for rendering text.
        /// </summary>
        [Browsable(true), DefaultValue(TextStyle.Normal), Category("Appearance"), Description("Font style for rendering text")]
        public TextStyle TextStyle
        {
            get { return mTextStyle; }
            set
            {
                mTextStyle = value;
                InvalidateRenderation();
            }
        }
        #endregion

        #region Title Text Style
        private TextStyle mTitleTextStyle = TextStyle.Normal;
        /// <summary>
        /// Gets/Sets font style for rendering text.
        /// </summary>
        [Browsable(true), DefaultValue(TextStyle.Normal), Category("Appearance"), Description("Font style for rendering title")]
        public TextStyle TitleTextStyle
        {
            get { return mTitleTextStyle; }
            set
            {
                mTitleTextStyle = value;
                InvalidateRenderation();
            }
        }
        #endregion

        #region Draw Check
        private bool mDrawCheck = false;
        /// <summary>
        /// Gets/Sets whether draw checkbox for Checked items.
        /// </summary>
        [Browsable(true),DefaultValue(false),Category("Appearance")]
        public bool DrawCheck
        {
            get { return mDrawCheck; }
            set { mDrawCheck = value; }
        }
        #endregion

        #region Draw Grid
        //private bool mDrawGrid = false;
        ///// <summary>
        ///// Gets/Sets whether draw grid when multi-column mode.
        ///// </summary>
        //[Browsable(true), DefaultValue(false), Category("Appearance")]
        //public bool DrawGrid
        //{
        //    get { return mDrawGrid; }
        //    set { mDrawGrid = value; }
        //}
        #endregion

        #region Sign Pad
        private int mSignPad = 18;
        /// <summary>
        /// Gets/Sets padding for plus/minus sign.
        /// </summary>
        [Browsable(true),DefaultValue(18),Category("Appearance")]
        public int SignPad
        {
            get { return mSignPad; }
            set
            {
                mSignPad = value;

                // invalidate layout
                InvalidateLayout();
            }
        }
        #endregion

        #region Padding
        private int mPadding = 3;
        /// <summary>
        /// Gets/Sets row padding.
        /// </summary>
        [Browsable(true),DefaultValue(3),Category("Appearance")]
        public int Padding
        {
            get { return mPadding; }
            set
            {
                mPadding = value;
                // invalidate layout
                InvalidateLayout();
            }
        }
        #endregion

        #region Selected Index
        /// <summary>
        /// Index of currently selected row
        /// </summary>
        protected int mSelectedIndex = -1;
        /// <summary>
        /// Gets/Sets index of selected item
        /// </summary>
        [Browsable(true), DefaultValue(-1), Category("Value")]
        public int SelectedIndex
        {
            get
            {
                return mSelectedIndex;
            }

            set
            {
                mSelectedIndex = value;
                if (mReflattenItems)
                {
                    // reflattern it
                    FlattenItems();
                }
                #region Defensive
                #endregion
                if ((mSelectedIndex >= 0) && (mSelectedIndex < mRenderContext.VisibleItems.Count))
                {
                    ListboxVisibleItemInfo visibleItem = (ListboxVisibleItemInfo)mRenderContext.VisibleItems[mSelectedIndex];
                    Text = visibleItem.Item.Text;
                    mSelectedValue = visibleItem.Item.Value;
                }
                else
                {
                    Text = "";
                    mSelectedValue = null;
                }

                OnSelectedIndexChanged();

                //raise event
                RaiseSelectedIndexChanged(EventArgs.Empty);

                // ensure the item
                EnsureVisible(0, mSelectedIndex);

                // draw again
                InvalidateRenderation();
            }
        }
        #endregion

        #region Selected value
        object mSelectedValue = null;
        /// <summary>
        /// Gets/Sets the value of currently selected item
        /// </summary>
        [Browsable(false), DefaultValue(null)]
        public object SelectedValue
        {
            get
            {
                return mSelectedValue;
            }
            set
            {
                SelectedItem = FindItem(value);
            }
        }

        #endregion

        #region Selected Item
        /// <summary>
        /// Gets/Sets the currently selected item. This method is safer and more correct than Items[SelectedIndex] when this list is a tree.
        /// </summary>
        [Browsable(false), DefaultValue(null)]
        public ListBoxItem SelectedItem
        {
            get { return GetSelectedItem(); }
            set { SetSelectedItem(value); }
        }

        
        #endregion

        #region Auto-Sized Column
        private int mAutoSizedColumn = 0;
        /// <summary>
        /// Gets/Sets index of column that will be auto-resized.
        /// </summary>
        [Browsable(true),DefaultValue(0),Category("Appearance")]
        public int AutoSizedColumn
        {
            get { return mAutoSizedColumn; }
            set { mAutoSizedColumn = value; }
        }
        #endregion

        #region Has Sub Items
        /// <summary>
        /// True if need to check Has Sub Items again.
        /// </summary>
        protected bool mRecalculateHasSubItems = true;
        bool mHasSubItems = false;

        /// <summary>
        /// Checks whether this listbox has sub-items => draw as tree instead of list
        /// </summary>
        protected bool HasSubItems
        {
            get
            {
                if (mRecalculateHasSubItems)
                {
                    mHasSubItems = FindSubItems();
                    mRecalculateHasSubItems = false;
                }
                return mHasSubItems;
            }
        }

        /// <summary>
        /// Iterate throw all items in main list to see if I need to draw as a tree instead of a list
        /// </summary>
        bool FindSubItems()
        {
            
            bool result = false;
            if (mItems != null)
            {
                for (int i = 0; i < mItems.Count; i++)
                {
                    if ((mItems[i].Children != null) && (mItems[i].Children.Count > 0))
                    {
                        //as long as any item has a children, that is enough to draw as tree instead of list
                        result = true;
                        break;
                    }
                }
            }
            return result;
        }
        #endregion

        #region Render Context
        private ListBoxRenderContext mRenderContext;
        /// <summary>
        /// Gets reference to current renderation context.
        /// </summary>
        protected ListBoxRenderContext RenderContext
        {
            get { return mRenderContext; }
        }
        #endregion

        #region Even Item Background Color
        Color mEvenItemBackgroundColor = Color.White;
        /// <summary>
        /// Gets or Sets background color of even items.
        /// </summary>
        [Browsable(true), Category("Appearance"), Description("Background color of even items.")]
        public Color EvenItemBackgroundColor
        {
            get { return mEvenItemBackgroundColor; }
            set { mEvenItemBackgroundColor = value; }
        }
        #endregion  

        #region Odd Item Backgound Color
        Color mOddItemBackgoundColor = Color.FromArgb(238, 238, 238);
        /// <summary>
        /// Gets or Sets background color of odd items.
        /// </summary>
        [Browsable(true), Category("Appearance"), Description("Background color of odd items.")]
        public Color OddItemBackgoundColor
        {
            get { return mOddItemBackgoundColor; }
            set { mOddItemBackgoundColor = value; }
        }
        #endregion  

        #endregion

        #region Ensure Visible
        /// <summary>
        /// Ensure visible for an row.
        /// </summary>
        /// <param name="ColumnIdx">Column index.</param>
        /// <param name="RowIdx">Row index</param>
        public void EnsureVisible(int ColumnIdx, int RowIdx)
        {
            if (mRenderContext.VisibleItems == null) return;

            #region invalidate layout to ensure calculation first
            // force to calculate again
            if (mRecalculateLayout)
            {
                OnCalculateLayout();
                mRecalculateLayout = false;
            }
            else
            {
                // do not calculate layout again
            }
            #endregion
            bool redraw = false;

            //ensure row is visible
            if ((RowIdx >= 0) && (RowIdx < mRenderContext.VisibleItems.Count))
            {
                int oy = RowIdx * mRowHeight - mRenderContext.OffsetY;
                if (mDrawTitle) oy += mTitleHeight;

                if (oy < mRenderContext.OriginTitleRegion.Bottom)//top is clipped
                {
                    mRenderContext.OffsetY = RowIdx * mRowHeight;
                    redraw = true;
                    //Log.Information("Top is clipped");
                }
                else
                {
                    if (oy > mRenderContext.OriginCellsRegion.Bottom - mRowHeight - 1) //bottom is clipped
                    {
                        //Log.Information("Bottom is clipped");
                        mRenderContext.OffsetY += (oy - (mRenderContext.OriginCellsRegion.Bottom - mRowHeight - 1));
                        redraw = true;
                    }
                }
            }//ensure row is visible

            if (redraw)
            {
                UpdateScrollbars();
                Invalidate();
            }
        }
        #endregion

        #region Expand /Collapse
        #region Expand
        /// <summary>
        /// Try to expand an item, expand its parents as well if they're collapsed
        /// </summary>
        public void Expand(ListBoxItem item)
        {
            ExpandItem(item);
            mReflattenItems = true;
            mRecalculateLayout = true;
            Invalidate();
        }

        void ExpandItem(ListBoxItem item)
        {
            if (!item.IsExpanded)
            {
                item.IsExpanded = true;

                //find parent item, expand it
                if ((item.mSiblings != null) && (item.mSiblings.Parent != null))
                {
                    ExpandItem(item.mSiblings.Parent);
                }
            }
        }
        #endregion

        #region Expand All
        /// <summary>
        /// Expand all items
        /// </summary>
        public void ExpandAll()
        {
            for (int i = 0; i < mItems.Count; i++)
            {
                ExpandAllItem(mItems[i]);
            }

            mReflattenItems = true;

            InvalidateLayout();
        }

        void ExpandAllItem(ListBoxItem item)
        {
            item.IsExpanded = true;
            if ((item.Children != null) && (item.Children.Count > 0))
            {
                for (int i = 0; i < item.Children.Count; i++)
                {
                    ExpandAllItem(item.Children[i]);
                }
            }
        }
        #endregion

        #region Collapse
        /// <summary>
        /// Collapse an item
        /// </summary>
        public void Collapse(ListBoxItem item)
        {
            if (item.IsExpanded)
            {
                item.IsExpanded = false;

                // reflatten item
                mReflattenItems = true;

                InvalidateLayout();
            }
        }
        #endregion

        #region Collapse All
        /// <summary>
        /// Collapse all items
        /// </summary>
        public void CollapseAll()
        {
            for (int i = 0; i < mItems.Count; i++)
            {
                CollapseAllItem(mItems[i]);
            }

            mReflattenItems = true;

            InvalidateLayout();
        }

        void CollapseAllItem(ListBoxItem item)
        {
            item.IsExpanded = false;
            if ((item.Children != null) && (item.Children.Count > 0))
            {
                for (int i = 0; i < item.Children.Count; i++)
                {
                    CollapseAllItem(item.Children[i]);
                }
            }
        }
        #endregion
        #endregion

        #region On Get Render Context
        /// <summary>
        /// Get reference to the renderation context which is used by <see cref="IControlRenderer"/>.
        /// </summary>
        protected override RenderContext OnGetRenderContext(Graphics canvas)
        {
            // calculate some value for rendering.

            #region calculate number of row will displayed
            mRenderContext.RowHeight = mRowHeight;
            int start= (int)(mRenderContext.OffsetY / mRowHeight);
            int count= (mRenderContext.OriginCellsRegion.Height / mRowHeight);
            int first = mRowHeight - (mRenderContext.OffsetY - start * mRowHeight);
            int rest = mRenderContext.OriginCellsRegion.Height - (count * mRowHeight + first);

            if (rest > 0) count += 2; // include first and last
            else count += 1; // include first only.

            if (count + start > mRenderContext.VisibleItems.Count)
            {
                count = mRenderContext.VisibleItems.Count - start;
            }
            #endregion

            mRenderContext.StartRowIndex = start;
            mRenderContext.RenderedRowCount = count;

            mRenderContext.SelectedItemIndex = mSelectedIndex;
            mRenderContext.HasSubItems = this.HasSubItems;

            mRenderContext.SignPad = mSignPad;
            mRenderContext.RowPaddding = mPadding;
            // calculate this
            return mRenderContext;
        }
        #endregion

        #region On Calculate Layout
        /// <summary>
        /// Perform layout calculations neccessary for this control.
        /// </summary>
        protected override void OnCalculateLayout()
        {
            //Rectangle rTrack = new Rectangle(0, 0, Width, Height);
            //int width = (int)(((double)mValue / ((double)mMaxValue - (double)mMinValue)) * rTrack.Width);
            //Rectangle rProgress = new Rectangle(rTrack.Left, rTrack.Top, width, rTrack.Height);

            ////assign to render context
            //mRenderContext.TrackRegion = rTrack;
            //mRenderContext.ProgressRegion = rProgress;

            bool mShowScrollbar = false;
            Rectangle rControl = new Rectangle(0, 0, Width, Height);
            // if item is not empty, check the scrollbar
            if (mItems != null)
            {
                // check if flattern items.
                if (mReflattenItems) FlattenItems();

                int totalHeight = mRenderContext.VisibleItems.Count * mRowHeight;
                if (mDrawTitle) totalHeight += mTitleHeight;

                
                #region Show/hide scrollbar

                if (vScroller != null)
                {
                    if (totalHeight > this.Height)
                    {
                        //show the scroll bar
                        vScroller.Height = Height;
                        vScroller.Left = rControl.Right - vScroller.Width;
                        vScroller.Top = rControl.Top;
                        vScroller.MaxValue = totalHeight + mRowHeight; // extra 1 line
                        vScroller.LargeChange = rControl.Height;
                        vScroller.SmallChange = mRowHeight;
                        vScroller.Visible = true;

                        mShowScrollbar = true;
                    }
                    else
                    {
                        vScroller.Visible = false;
                        mShowScrollbar = false;
                    }
                }
                #endregion
            }

            #region For title region
            int titleHeight = 0;
            int titleWidth = rControl.Width;
            if (mDrawTitle)
            {
                #region Show title
                titleHeight = mTitleHeight;
                #endregion
            }
            #region Defensive tracing
            else
            {

            }
            #endregion
            if (mShowScrollbar)
            {
                #region Show scrollbar
                titleWidth -= vScroller.Width;
                #endregion
            }
            #region Defensive tracing
            else
            {

            }
            #endregion

            // initialize rTitle
            mRenderContext.OriginTitleRegion = new Rectangle(rControl.Left, rControl.Top, titleWidth, titleHeight);
            #endregion

            #region For main region
            int cellsWidth = titleWidth;
            int cellsHeight = rControl.Height - titleHeight;
            mRenderContext.OriginCellsRegion = new Rectangle(rControl.Left, mRenderContext.OriginTitleRegion.Bottom, cellsWidth, cellsHeight);
            #endregion

            AutoResizeColumns();
        }

        #region Auto Resize Columns
        /// <summary>
        /// Adjust one column (specified by mAutosizeColumn) to fit
        /// </summary>
        protected virtual void AutoResizeColumns()
        {
            int size = mRenderContext.OriginCellsRegion.Width;
            if (mColumns != null)
            {
                for (int i = 0; i < mColumns.Count; i++)
                {
                    if (i != mAutoSizedColumn)
                    {
                        size -= mColumns[i].Width;
                    }
                }

                if ((mAutoSizedColumn >= 0) && (mAutoSizedColumn < mColumns.Count))
                {
                    mColumns[mAutoSizedColumn].Width = size;
                }
            }
        }
        #endregion
        #endregion

        #region On Selected Index Changed
        /// <summary>
        /// Raise when SelectedIndex has been changed.
        /// </summary>
        protected virtual void OnSelectedIndexChanged()
        {
        }
        #endregion

        #region On Render Item Content
        #endregion

        #region IListboxHost Members

        /// <summary>
        /// Notify that item has been modified.
        /// </summary>
        /// <param name="item"></param>
        void IListBoxHost.NotifyItemModified(ListBoxItem item)
        {
            mRecalculateHasSubItems = true;
            mReflattenItems = true;
            mRenderRequired = true;

            // reflattern
            FlattenItems();
            InvalidateLayout();
        }

        /// <summary>
        /// Notify that new item has been added.
        /// </summary>
        /// <param name="parent">Parent element.</param>
        /// <param name="newItem">New item has been added.</param>
        void IListBoxHost.NotifyItemAdded(ListBoxItemList parent, ListBoxItem newItem)
        {
            mRecalculateHasSubItems = true;
            mReflattenItems = true;
            mRenderRequired = true;

            // reflattern
            FlattenItems();

            InvalidateLayout();
        }

        /// <summary>
        /// Notify that an item has been removed from parent.
        /// </summary>
        /// <param name="parent">Parent that item removed from.</param>
        /// <param name="removedItem">Remove item.</param>
        void IListBoxHost.NotifyItemRemoved(ListBoxItemList parent, ListBoxItem removedItem)
        {
            mRecalculateHasSubItems = true;
            mReflattenItems = true;
            mRenderRequired = true;

            // reflattern
            FlattenItems();
            InvalidateLayout();
        }

        /// <summary>
        /// Nofity that all items has been cleared.
        /// </summary>
        /// <param name="list">Item list.</param>
        void IListBoxHost.NotifyItemsCleared(ListBoxItemList list)
        {
            mRecalculateHasSubItems = true;
            mReflattenItems = true;
            mRenderRequired = true;

            mSelectedIndex = -1;
            if (vScroller != null)
            {
                vScroller.Value = 0;
                mRenderContext.OffsetY = 0;
            }
            #region Defensive tracing
            else
            {
                // do not set here.
            }
            #endregion

            if (hScroller != null)
            {
                hScroller.Value = 0;                
            }
            #region Defensive tracing
            else
            {
                // do not set here.
            }
            #endregion

            // reflattern
            FlattenItems();
            InvalidateLayout();
        }

        /// <summary>
        /// Expand an listbox item.
        /// </summary>
        void IListBoxHost.Expand(ListBoxItem item)
        {
            Expand(item);
        }

        /// <summary>
        /// Collapse and listbox item.
        /// </summary>
        void IListBoxHost.Collapse(ListBoxItem item)
        {
            Collapse(item);
        }
        #endregion

        //============== PRIVATE METHODS

        #region Flattern
        /// <summary>
        /// Flattern items for drawing.
        /// </summary>
        protected void FlattenItems()
        {
            mRenderContext.VisibleItems.Clear();

            int count = mItems.Count;
            for (int i = 0; i < count; i++)
            {
                AddFlattenedItem(mItems[i], 1, i == count - 1, i == 0);
            }

            mReflattenItems = false;
        }

        void AddFlattenedItem(ListBoxItem item, int level, bool LastSibling, bool FirstSibling)
        {
            ListboxVisibleItemInfo vi = new ListboxVisibleItemInfo(item, level);
            vi.LastSibling = LastSibling;
            vi.FirstSibling = FirstSibling;
            mRenderContext.VisibleItems.Add(vi);

            //add sub items
            if ((item.Children != null) && (item.IsExpanded))
            {
                int count = item.Children.Count;
                for (int i = 0; i < count; i++)
                {
                    AddFlattenedItem(item.Children[i], level + 1, i == count - 1, i == 0);
                }
            }
        }
        #endregion

        #region Find Item
        /// <summary>
        /// Iterate through all item, find an item that has the specified value. Use reflection if mLookupField is set
        /// </summary>
        /// <param name="value">This is the value stored in item.Value, not the ListboxItem itself</param>
        public virtual ListBoxItem FindItem(object value)
        {
            ListBoxItem result = null;

            if (value != null)
            {
                //search for matching item
                for (int i = 0; i < mItems.Count; i++)
                {
                    ListBoxItem li = FindMatchingItem(mItems[i], value);
                    if (li != null)
                    {
                        result = li;
                        break;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Use recursive to find a matching item
        /// </summary>
        ListBoxItem FindMatchingItem(ListBoxItem item, object value)
        {
            ListBoxItem result = null;

            if (IsMatch(item.Value, value)) result = item;
            else
            {
                if ((item.Children != null) && (item.Children.Count > 0))
                {
                    for (int i = 0; i < item.Children.Count; i++)
                    {
                        ListBoxItem li = FindMatchingItem(item.Children[i], value);
                        if (li != null)
                        {
                            result = li;
                            break;
                        }
                    }//for
                }//if
            }//else

            return result;
        }

        /// <summary>
        /// Determine whether an item's value is same as the value. Use reflection if mLookupMember is set
        /// </summary>
        bool IsMatch(object itemValue, object value)
        {
            bool result = false;

            if ((itemValue == null) && (value == null)) result = true;
            else if (itemValue != null)
            {
                result = itemValue.Equals(value);
                #region Origin code
                //if (mLookupField == "")
                //{
                //    //result = (itemValue == value); //Inequality Equality problem
                //    result = itemValue.Equals(value);
                //}
                //else
                //{
                //    //reflect for the lookup member
                //    mLookupMember = null;
                //    ObjectReflector reflector = new ObjectReflector();
                //    MemberInfoCollection members = reflector.GetMembers(itemValue.GetType(), true, true, null, false, true);

                //    for (int i = 0; i < members.Count; i++)
                //    {
                //        if (members[i].Name == mLookupField)
                //        {
                //            mLookupMember = members[i];
                //            break;
                //        }
                //    }

                //    if (mLookupMember != null)
                //    {
                //        //reflect value
                //        object obj = null;
                //        if (mLookupMember is FieldInfo)
                //        {
                //            FieldInfo fi = mLookupMember as FieldInfo;
                //            obj = fi.GetValue(itemValue);
                //        }
                //        else if (mLookupMember is PropertyInfo)
                //        {
                //            PropertyInfo pi = mLookupMember as PropertyInfo;
                //            obj = pi.GetValue(itemValue, null);
                //        }

                //        //compare value
                //        //if (obj != null) result = (value == obj); //Inequality Equality problem
                //        if (obj != null) result = value.Equals(obj);
                //    }
                //}

                #endregion
            }

            return result;
        }
        #endregion

        #region Get/Set Selected Listbox Item
        /// <summary>
        /// Get selected item.
        /// </summary>
        protected virtual ListBoxItem GetSelectedItem()
        {
            ListBoxItem result = null;

            if ((mSelectedIndex >= 0) && (mSelectedIndex < mRenderContext.VisibleItems.Count))
            {
                result = ((ListboxVisibleItemInfo)(mRenderContext.VisibleItems[mSelectedIndex])).Item;
            }
            #region Defensive
            else
            {
                // no item is selected.
            }
            #endregion

            return result;
        }

        /// <summary>
        /// Set selected item.
        /// </summary>
        protected virtual void SetSelectedItem(ListBoxItem item)
        {
            if (item != null)
            {
                //make sure I'm visible
                if ((item.mSiblings != null) && (item.mSiblings.Parent != null))
                {
                    ExpandItem(item.mSiblings.Parent);
                }

                //force flatten
                FlattenItems();

                //find my index
                int idx = 0;
                bool next = true;

                while ((idx < mRenderContext.VisibleItems.Count) && next)
                {
                    if (((ListboxVisibleItemInfo)(mRenderContext.VisibleItems[idx])).Item == item)
                    {
                        next = false;
                    }
                    else idx++;
                }

                if (next) SelectedIndex = -1;//could not find matching item
                else
                {
                    SelectedIndex = idx;
                }
            }
            else
            {//item == null
                SelectedIndex = -1;
            }
        }

        #region Set Selected Index
        /// <summary>
        /// This is similar to setting property SelectedIndex.
        /// However, using this method will not raise OnSelectedIndexChanged event
        /// </summary>
        public void SetSelectedIndex(int Idx)
        {
            mSelectedIndex = Idx;
            if ((mSelectedIndex >= 0) && (mSelectedIndex < mRenderContext.VisibleItems.Count))
            {
                ListboxVisibleItemInfo visibleItem = (ListboxVisibleItemInfo)mRenderContext.VisibleItems[mSelectedIndex];
                Text = visibleItem.Item.Text;                
                mSelectedValue = visibleItem.Item.Value;
            }

            EnsureVisible(0, mSelectedIndex);
            Invalidate();
        }
        #endregion

        #endregion

        #region Scrollers handling
        /// <summary>
        /// Vertical scroller.
        /// </summary>
        Scroller vScroller = new Scroller();

        /// <summary>
        /// Horizontal scroller.
        /// </summary>
        Scroller hScroller = new Scroller();

        /// <summary>
        /// Initialize scrollers.
        /// </summary>
        private void InitializeScrollers()
        {
            vScroller = new Scroller();
            vScroller.LayoutStyle = ScrollerStyle.Vertical;
            vScroller.Visible = false;
            vScroller.Parent = this;

            vScroller.GotFocus += new EventHandler(vScroller_GotFocus);
            vScroller.Scroll += new System.Windows.Forms.ScrollEventHandler(vScroller_Scroll);
            vScroller.DialogKeyPressed += new ProcessDialogKeyHandler(vScroller_DialogKeyPressed);
        }

        void vScroller_GotFocus(object sender, EventArgs e)
        {
            this.Focus();
        }

        /// <summary>
        /// Update scrollbars to current view.
        /// </summary>
        private void UpdateScrollbars()
        {
            vScroller.SetValue(mRenderContext.OffsetY);
        }

        bool vScroller_DialogKeyPressed(object sender, System.Windows.Forms.Keys keyData)
        {
            return false;//tell the scrollbar not to process dialog key by itself
        }

        void vScroller_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
        {
            mRenderContext.OffsetY = vScroller.Value;
            InvalidateRenderation();
        }
        #endregion

        #region Got/Lost Focus
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            InvalidateRenderation();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            InvalidateRenderation();
        }
        #endregion

        #region Mouse/keyboard event

        #region HitTest
        ListboxRegion mCurrentRegion = ListboxRegion.Unknown;
        protected int mRowUnderHit = 0;

        /// <summary>
        /// Hit test at position
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        ListboxRegion HitTest(int x, int y)
        {
            ListboxRegion region = ListboxRegion.Unknown;

            if (mRenderContext.OriginTitleRegion .Contains(x, y)) region = ListboxRegion.Title;
            else if (mRenderContext.OriginCellsRegion .Contains(x, y)) region = ListboxRegion.Cells;

            if (region != ListboxRegion.Cells) mRowUnderHit = -1;
            else
            {//find row index under hit point
                int oy = y + mRenderContext.OffsetY - mRenderContext.OriginTitleRegion.Height;
                mRowUnderHit = oy / mRowHeight;
            }

            return region;
        }
        #endregion

        #region On mouse leave
        protected override void OnMouseLeave(EventArgs e)
        {
            mRenderContext.HoverIndex = -1;
            base.OnMouseLeave(e);
            InvalidateRenderation();
        }
        #endregion

        #region On mouse move
        /// <summary>
        /// Current mouse's X-coordinate
        /// </summary>
        int CurrentX;
        protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
        {
            int OldRowUnderHit = mRowUnderHit;
            CurrentX = e.X;
            ListboxRegion rewRegion = HitTest(e.X, e.Y);

            if ((mCurrentRegion != rewRegion) || (OldRowUnderHit != mRowUnderHit))
            {
                mCurrentRegion = rewRegion;
                mRenderContext.HoverIndex = mRowUnderHit;
                
                InvalidateRenderation();
            }
        }
        #endregion

        #region On click
        protected override void OnClick(EventArgs e)
        {
            if (!Focused) Focus();
            #region Check if expand or select
            if (mItems != null)
            {
                if (mItems.Count > 0)
                {
                    if ((mRowUnderHit >= 0) && (mRowUnderHit < mRenderContext.VisibleItems.Count))
                    {
                        ListboxVisibleItemInfo visibleItem = (ListboxVisibleItemInfo)mRenderContext.VisibleItems[mRowUnderHit];
                        ListBoxItem item = visibleItem.Item;
                        if ((item.Children != null) && (item.Children.Count > 0))
                        {//has children
                            if (CurrentX < mPadding + mSignPad * visibleItem.Level)
                            {//click on sign
                                if (item.IsExpanded) Collapse(item);
                                else Expand(item);
                            }
                            else
                            {
                                SelectedIndex = mRowUnderHit;
                            }
                        }
                        else
                        {
                            SelectedIndex = mRowUnderHit;
                        }
                    }
                }
            }
            #endregion

            #region Check item click event
            if (mCurrentRegion == ListboxRegion.Cells)
            {
                RaiseItemClicked(e);
            }
            #endregion

            base.OnClick(e);
        }
        #endregion

        #region On double click

        protected override void OnDoubleClick(EventArgs e)
        {
            if (!Focused) Focus();
            #region Check if expand or select
            if (mItems != null)
            {
                if (mItems.Count > 0)
                {
                    if ((mRowUnderHit >= 0) && (mRowUnderHit < mRenderContext.VisibleItems.Count))
                    {
                        ListboxVisibleItemInfo visibleItem = (ListboxVisibleItemInfo)mRenderContext.VisibleItems[mRowUnderHit];
                        ListBoxItem item = visibleItem.Item;
                        if ((item.Children != null) && (item.Children.Count > 0))
                        {
                            //has children
                            if (item.IsExpanded) Collapse(item);
                            else Expand(item);
                        }
                        else
                        {
                            SelectedIndex = mRowUnderHit;
                        }
                    }
                }
            }
            #endregion

            base.OnDoubleClick(e);
        }
        #endregion

        #region On mouse wheel
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (vScroller != null) vScroller.ProcessMouseWheel(e);
        }
        #endregion

        #region Keyboard events
        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (!Focused) Focus();

            switch (keyData)
            {
                case Keys.Left:
                    if (SelectedItem != null)
                    {
                        if (SelectedItem.Children.Count > 0)
                        {
                            Collapse(SelectedItem);
                        }
                        #region Defensive
                        else
                        {
                            // skip this key.
                        }
                        #endregion
                    }
                    break;
                case Keys.Right:
                    if (SelectedItem != null)
                    {
                        if (SelectedItem.Children.Count > 0)
                        {
                            Expand(SelectedItem);
                        }
                        #region Defensive
                        else
                        {
                            // skip this key.
                        }
                        #endregion
                    }
                    break;
                case Keys.Up:
                    if (mSelectedIndex > 0)
                    {
                        SelectedIndex--;
                        //SetSelectedIndex(mSelectedIndex - 1);
                    }
                    break;
                case Keys.Down:
                    if (mSelectedIndex < mRenderContext.VisibleItems.Count - 1)
                    {
                        if (mSelectedIndex < 0) SelectedIndex = 0;
                        else SelectedIndex = mSelectedIndex + 1;
                        //if (mSelectedIndex < 0) SetSelectedIndex(0);
                        //else SetSelectedIndex(mSelectedIndex + 1);
                    }
                    break;
                default:
                    return base.ProcessDialogKey(keyData);
                    break;
            }
            return true;//key handled
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                e.Handled = true;
                RaiseSelectedIndexChanged(EventArgs.Empty);
            }
            else base.OnKeyPress(e);
        }
        #endregion

        #endregion

        #region Listbox region
        /// <summary>
        /// Region that hit test detect.
        /// </summary>
        internal enum ListboxRegion
        {
            Title,
            Cells,
            Unknown
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public ListBox()
        {
            mRenderContext = new ListBoxRenderContext();
            this.Size = new Size(170, 90);

            InitializeScrollers();
        }
        #endregion
    }

    #region Visible item info
    /// <summary>
    /// Item that visible to user.
    /// </summary>
    public class ListboxVisibleItemInfo
    {
        #region Fields
        public ListBoxItem Item;

        /// <summary>
        /// Level of nesting
        /// </summary>
        public int Level;

        /// <summary>
        /// Am I the last sibling item?
        /// </summary>
        public bool LastSibling = false;

        /// <summary>
        /// Am I the very first sibling item?
        /// </summary>
        public bool FirstSibling = false;

        /// <summary>
        /// Content position.
        /// </summary>
        public int ContentLeft = 0;
        #endregion

        #region Constructor
        public ListboxVisibleItemInfo(ListBoxItem item, int level)
        {
            this.Item = item;
            this.Level = level;
        }
        #endregion
    }

    /// <summary>
    /// Strongly typed collection of <see cref="ListboxVisibleItemInfo"/>.
    /// </summary>
    public class ListboxVisibleItemInfoList : IList, ICollection, IEnumerable
    {
        #region Fields
        /// <summary>
        /// internal data
        /// </summary>
        internal ListboxVisibleItemInfo[] mItems = null;
        /// <summary>
        /// Number of items currently in list
        /// </summary>
        internal int mCount = 0;

        [NonSerialized]
        private object mSynchRoot;
        #endregion

        #region Capacity
        private int mCapacity;
        /// <summary>
        /// Gets current maximal capacity
        /// </summary>
        public int Capacity
        {
            get { return mCapacity; }
        }
        #endregion

        #region IList Members
        #region Add
        /// <summary>
        /// Adds an item to the list
        /// </summary>
        public int Add(ListboxVisibleItemInfo value)
        {
            //check if not enough space then allocate more
            if (mCount >= mCapacity)
            {
                //calculate new capacity
                int newCapacity;
                if (mCapacity < 1000) newCapacity = (int)(mCapacity * 2);
                else if (mCapacity < 5000) newCapacity = (int)(mCapacity * 1.5);
                else newCapacity = (int)(mCapacity * 1.2);

                //allocate new buffer
                ListboxVisibleItemInfo[] newItems = new ListboxVisibleItemInfo[newCapacity];

                //copy old values to new buffer
                mItems.CopyTo(newItems, 0);

                //assign the new bufer to current buffer
                mItems = newItems;
                mCapacity = newCapacity;
            }

            //assign the new value to the end of current buffer
            mItems[mCount] = value;
            mCount++;

            return mCount - 1;
        }

        /// <summary>
        /// Adds an item to the list
        /// </summary>
        int IList.Add(object value)
        {
            return Add((ListboxVisibleItemInfo)value);
        }
        #endregion

        #region Clear
        /// <summary>
        /// Removes all items
        /// </summary>
        public void Clear()
        {
            Array.Clear(mItems, 0, mCount);
            mCount = 0;
        }
        #endregion

        #region Contains
        /// <summary>
        /// Determines whether the list contains a specific value
        /// </summary>
        public bool Contains(object value)
        {
            if (value == null)
            {
                //search for the first occurence of null item
                for (int i = 0; i < mCount; i++)
                {
                    if (mItems[i] == null) return true;
                }
            }
            else
            {
                //search for the first match
                for (int i = 0; i < mCount; i++)
                {
                    if (value.Equals(mItems[i])) return true;
                }
            }

            return false;
        }
        #endregion

        #region Index Of
        /// <summary>
        /// Determines the index of a specific item
        /// </summary>
        public int IndexOf(ListboxVisibleItemInfo value)
        {
            if (value == null)
            {
                //search for the first occurence of null item
                for (int i = 0; i < mCount; i++)
                {
                    if (mItems[i] == null) return i;
                }
            }
            else
            {
                //search for the first match
                for (int i = 0; i < mCount; i++)
                {
                    if (value.Equals(mItems[i])) return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// Determines the index of a specific item
        /// </summary>
        int IList.IndexOf(object value)
        {
            return IndexOf((ListboxVisibleItemInfo)value);
        }
        #endregion

        #region Insert
        /// <summary>
        /// Inserts an item to the list at the specified index
        /// </summary>
        public void Insert(int index, ListboxVisibleItemInfo value)
        {
            //check if not enough space then allocate more
            if (mCount >= mCapacity)
            {
                //calculate new capacity
                int newCapacity;
                if (mCapacity < 1000) newCapacity = (int)(mCapacity * 2);
                else if (mCapacity < 5000) newCapacity = (int)(mCapacity * 1.5);
                else newCapacity = (int)(mCapacity * 1.2);

                //allocate new buffer
                ListboxVisibleItemInfo[] newItems = new ListboxVisibleItemInfo[newCapacity];

                //copy old values from left of index to new buffer
                if (index > 0)
                {
                    Array.Copy(mItems, 0, newItems, 0, index);
                }

                //assign the new value to the index position of the new buffer
                newItems[index] = value;

                //copy old values from right of index to new buffer
                if (index < mCount)
                {
                    Array.Copy(mItems, index, newItems, index + 1, mCount - index);
                }

                //assign the new bufer to current buffer
                mItems = newItems;
                mCapacity = newCapacity;
                mCount++;
            }
            else
            {
                //copy old values from right of index to new buffer
                if (index < mCount)
                {
                    Array.Copy(mItems, index, mItems, index + 1, mCount - index);
                }

                //assign the new value to the index position of the new buffer
                mItems[index] = value;
                mCount++;
            }
        }

        /// <summary>
        /// Inserts an item to the list at the specified index
        /// </summary>
        void IList.Insert(int index, object value)
        {
            Insert(index, (ListboxVisibleItemInfo)value);
        }
        #endregion

        #region Is Fixed Size
        /// <summary>
        /// Gets a value indicating whether the list has a fixed size.
        /// </summary>
        public bool IsFixedSize
        { get { return false; } }
        #endregion

        #region Is Read Only
        /// <summary>
        /// Gets a value indicating whether the list is read-only.
        /// </summary>
        public bool IsReadOnly
        { get { return false; } }
        #endregion

        #region Remove
        /// <summary>
        /// Removes the first occurrence of a specific object from the list.
        /// </summary>
        public void Remove(ListboxVisibleItemInfo value)
        {
            int index = IndexOf(value);
            if (index >= 0) RemoveAt(index);
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the list.
        /// </summary>
        void IList.Remove(object value)
        {
            int index = IndexOf((ListboxVisibleItemInfo)value);
            if (index >= 0) RemoveAt(index);
        }
        #endregion

        #region Remove At
        /// <summary>
        /// Removes the list item at the specified index
        /// </summary>
        public void RemoveAt(int index)
        {
            mCount--;
            if (index < mCount)
            {
                //copy old values from the right of index
                Array.Copy(mItems, index + 1, mItems, index, mCount - index);
            }
            mItems[mCount] = null;
        }
        #endregion

        #region Indexer
        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        public ListboxVisibleItemInfo this[int index]
        {
            get { return mItems[index]; }
            set { mItems[index] = value; }
        }

        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        object IList.this[int index]
        {
            get { return mItems[index]; }
            set { mItems[index] = (ListboxVisibleItemInfo)value; }
        }
        #endregion

        #endregion

        #region ICollection Members
        #region Copy To
        /// <summary>
        /// Copies the elements of the collection to an array
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from collection. The array must have zero-based indexing.</param>
        public void CopyTo(Array array)
        {
            Array.Copy(mItems, 0, array, 0, mCount);
        }

        /// <summary>
        /// Copies the elements of the collection to an array, starting at a particular array index. 
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from collection. The array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        public void CopyTo(Array array, int index)
        {
            Array.Copy(mItems, 0, array, index, mCount);
        }

        /// <summary>
        /// Copies the elements of the collection to an array, starting at a particular array index. 
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from collection. The array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in this collection at which copying begins.</param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        /// <param name="count">The number of elements to copy.</param>
        public void CopyTo(int index, Array array, int arrayIndex, int count)
        {
            Array.Copy(mItems, index, array, arrayIndex, count);
        }
        #endregion

        #region Count
        /// <summary>
        /// Gets the number of elements contained in the ICollection.
        /// </summary>
        public int Count
        { get { return mCount; } }
        #endregion

        #region Is Synchronized
        /// <summary>
        /// Gets a value indicating whether access to the ICollection is synchronized (thread safe).
        /// </summary>
        public bool IsSynchronized
        { get { return false; } }
        #endregion

        #region SyncRoot
        /// <summary>
        /// Gets an object that can be used to synchronize access to the ICollection.
        /// </summary>
        public object SyncRoot
        {
            get
            {
                if (mSynchRoot == null)
                {
                    System.Threading.Interlocked.CompareExchange(ref mSynchRoot, new object(), null);
                }
                return mSynchRoot;
            }
        }
        #endregion

        #endregion

        #region IEnumerable Members

        #region Get Enumerator
        /// <summary>
        /// Returns an enumerator that iterates through this collection
        /// </summary>
        public IEnumerator GetEnumerator()
        {
            return new ListboxVisibleItemInfoListEnumerator(this);
        }

        #region Enumerator
        /// <summary>
        /// Strongly typed enumerator of ListboxVisibleItemInfo.
        /// </summary>
        public class ListboxVisibleItemInfoListEnumerator : object, System.Collections.IEnumerator
        {
            #region privates
            /// <summary>
            /// Current index
            /// </summary>
            private int mIndex;

            /// <summary>
            /// Current element pointed to.
            /// </summary>
            private ListboxVisibleItemInfo mCurrentElement;

            /// <summary>
            /// Collection to enumerate.
            /// </summary>
            private ListboxVisibleItemInfoList mCollection;
            #endregion

            #region Constructor
            /// <summary>
            /// Default constructor for enumerator.
            /// </summary>
            /// <param name="collection">Instance of the collection to enumerate.</param>
            internal ListboxVisibleItemInfoListEnumerator(ListboxVisibleItemInfoList collection)
            {
                mIndex = -1;
                mCollection = collection;
            }
            #endregion

            #region Current
            /// <summary>
            /// Gets the ListboxVisibleItemInfo object in the enumerated ListboxVisibleItemInfoCollection currently indexed by this instance.
            /// </summary>
            public ListboxVisibleItemInfo Current
            { get { return mCurrentElement; } }

            /// <summary>
            /// Gets the current element in the collection.
            /// </summary>
            object System.Collections.IEnumerator.Current
            { get { return mCurrentElement; } }
            #endregion

            #region Reset
            /// <summary>
            /// Reset the cursor, so it points to the beginning of the enumerator.
            /// </summary>
            public void Reset()
            {
                mIndex = -1;
                mCurrentElement = null;
            }
            #endregion

            #region MoveNext
            /// <summary>
            /// Advances the enumerator to the next queue of the enumeration, if one is currently available.
            /// </summary>
            /// <returns>true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration.</returns>
            public bool MoveNext()
            {
                mIndex++;
                if (mIndex < mCollection.mCount)
                {
                    mCurrentElement = mCollection.mItems[mIndex];
                    return true;
                }
                mIndex = mCollection.mCount;
                return false;
            }
            #endregion
        }
        #endregion

        #endregion

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// <para>Default capacity is 50</para>
        /// </summary>
        public ListboxVisibleItemInfoList()
        {
            mCapacity = 50;
            mItems = new ListboxVisibleItemInfo[mCapacity];
        }

        /// <summary>
        /// Create a new instance with the specified capacity
        /// </summary>
        public ListboxVisibleItemInfoList(int capacity)
        {
            mCapacity = capacity;
            mItems = new ListboxVisibleItemInfo[capacity];
        }
        #endregion
    }
    #endregion
}
